package emu.decvices.joy;

import emu.decvices.bus.BusDevice;

import java.awt.event.KeyEvent;
import java.io.Serializable;

/**
 * 手柄
 */
public class JoyPad extends BusDevice {
    public static final int JOY_1P = 0x00;
    public static final int JOY_2P = 0x01;
    private static final long X_Y_Pressed_Interval_Time=50;//连发间隔时间,单位是毫秒

    //手柄对应按键在配置数组中的下标位置
    public static final int JOY_RIGHT = 0x00;
    public static final int JOY_LEFT = JOY_RIGHT + 1;
    public static final int JOY_DOWN = JOY_LEFT + 1;
    public static final int JOY_UP = JOY_DOWN + 1;
    public static final int JOY_START = JOY_UP + 1;
    public static final int JOY_SELECT = JOY_START + 1;
    public static final int JOY_B = JOY_SELECT + 1;
    public static final int JOY_A = JOY_B + 1;
    public static final int JOY_Y = JOY_A + 1;
    public static final int JOY_X = JOY_Y + 1;
    //X键是否按下(A的连发功能)
    private boolean x_Pressed = false;
    //Y键是否按下(B的连发功能)
    private boolean y_Pressed = false;
    //按下时的时间戳
    private long X_Y_Pressed_start;
    private boolean X_Y_Pressed = true;//A,B连发时是否模拟按下(连发时该值每200毫秒切换一次)


    //两个手柄的默认按键配置，数组中按键顺序:右，左，下，上，START,SELECT,B,A,Y,X
    public static final int[][] joyPadIndex = {
            {KeyEvent.VK_D, KeyEvent.VK_A, KeyEvent.VK_S, KeyEvent.VK_W, KeyEvent.VK_H, KeyEvent.VK_G, KeyEvent.VK_K, KeyEvent.VK_L, KeyEvent.VK_I, KeyEvent.VK_O},
            {KeyEvent.VK_RIGHT, KeyEvent.VK_LEFT, KeyEvent.VK_DOWN, KeyEvent.VK_UP, KeyEvent.VK_DECIMAL, KeyEvent.VK_NUMPAD0, KeyEvent.VK_NUMPAD1, KeyEvent.VK_NUMPAD2, KeyEvent.VK_NUMPAD4, KeyEvent.VK_NUMPAD5}
    };
    //两个手柄在CPU总线上的地址
    private int[] address = {0x4016, 0x4017};
    //当前手柄索引，对应常量JOY_1P|JOY_2P
    private int joyIndex;
    //锁定手柄按键，用于让CPU读取锁定前按钮状态
    private static boolean strobe;
    //由于CPU一次仅读取一个按钮，因此要记录读取到第几个按钮(对应上面配置数组的顺序)
    private int index;
    //当前手柄按钮是否按下的情况(实时变化)
    private int joy = 0x00;
    /*
        锁定时记录joy的值，用于共CPU读取
        CPU每次读取前都会先发送信号锁定手柄，此时将joy值赋值给joyLatch，用于共CPU读取锁定前最后的手柄状态
        而joy可以实时记录手柄状态
     */
    private int joyLatch = 0x00;//锁定的状态

    public JoyPad(int joyIndex) {
        this.joyIndex = joyIndex;
    }

    private KeyAdapter keyAdapter = new KeyAdapter() {
        /*
            在Nintendo Entertainment System (NES)中，CPU通过一个特殊的内存地址读取控制器的状态。这个特殊的内存地址是$4016和$4017，分别对应于控制器1和控制器2。

            当CPU写入任何值到$4016，控制器会被'latch'，也就是说，它会把当前的按键状态锁定，并准备通过数据线发送给CPU。

            然后，每当CPU从$4016或$4017读取数据时，控制器会发送一个bit，表示一个按键的状态（1表示按下，0表示未按下）。这个过程会重复8次，每次发送一个不同按键的状态。

            下面是按键状态发送的顺序（从最先发送到最后发送）：

            A
            B
            Select
            Start
            Up
            Down
            Left
            Right
            因此，如果玩家按下了A和Start键，那么CPU在读取$4016八次后，会得到如下的数据：

            1
            0
            0
            1
            0
            0
            0
            0
            这就是NES中CPU如何读取手柄控制器的。
         */
        @Override
        public void keyPressed(KeyEvent e) {
            if (e.getKeyCode() == joyPadIndex[joyIndex][JOY_A]) {
                joy |= 0x01;
            } else if (e.getKeyCode() == joyPadIndex[joyIndex][JOY_B]) {
                joy |= 0x02;
            } else if (e.getKeyCode() == joyPadIndex[joyIndex][JOY_SELECT]) {
                joy |= 0x04;
            } else if (e.getKeyCode() == joyPadIndex[joyIndex][JOY_START]) {
                joy |= 0x08;
            } else if (e.getKeyCode() == joyPadIndex[joyIndex][JOY_UP]) {
                joy |= 0x10;
            } else if (e.getKeyCode() == joyPadIndex[joyIndex][JOY_DOWN]) {
                joy |= 0x20;
            } else if (e.getKeyCode() == joyPadIndex[joyIndex][JOY_LEFT]) {
                joy |= 0x40;
            } else if (e.getKeyCode() == joyPadIndex[joyIndex][JOY_RIGHT]) {
                joy |= 0x80;
            } else if (e.getKeyCode() == joyPadIndex[joyIndex][JOY_X]) {
                if(x_Pressed){
                    return;
                }
                x_Pressed = true;
                X_Y_Pressed = true;
                X_Y_Pressed_start = System.currentTimeMillis();
            } else if (e.getKeyCode() == joyPadIndex[joyIndex][JOY_Y]) {
                if(y_Pressed){
                    return;
                }
                y_Pressed = true;
                X_Y_Pressed = true;
                X_Y_Pressed_start = System.currentTimeMillis();
            }
        }

        @Override
        public void keyReleased(KeyEvent e) {
            if (e.getKeyCode() == joyPadIndex[joyIndex][JOY_A]) {
                joy &= ~0x01;
            } else if (e.getKeyCode() == joyPadIndex[joyIndex][JOY_B]) {
                joy &= ~0x02;
            } else if (e.getKeyCode() == joyPadIndex[joyIndex][JOY_SELECT]) {
                joy &= ~0x04;
            } else if (e.getKeyCode() == joyPadIndex[joyIndex][JOY_START]) {
                joy &= ~0x08;
            } else if (e.getKeyCode() == joyPadIndex[joyIndex][JOY_UP]) {
                joy &= ~0x10;
            } else if (e.getKeyCode() == joyPadIndex[joyIndex][JOY_DOWN]) {
                joy &= ~0x20;
            } else if (e.getKeyCode() == joyPadIndex[joyIndex][JOY_LEFT]) {
                joy &= ~0x40;
            } else if (e.getKeyCode() == joyPadIndex[joyIndex][JOY_RIGHT]) {
                joy &= ~0x80;
            } else if (e.getKeyCode() == joyPadIndex[joyIndex][JOY_X]) {
                x_Pressed = false;
            } else if (e.getKeyCode() == joyPadIndex[joyIndex][JOY_Y]) {
                y_Pressed = false;
            }
        }
    };

    @Override
    public int fromDeviceRead(int address) {
        if (address == this.address[joyIndex]) {
            if (!strobe) {
                if (index > 7) {
                    return joyLatch & 0x01;
                }
                int d = joyLatch >> index++;
                if (d == 1) {
                }
                return d & 0x01;
            } else {
                index = 0;
                return joy & 0x01;
            }
        }
        return -1;
    }

    @Override
    public boolean toDeviceWrite(int address, int data) {
        if (address == 0x4016) {
            strobe = (data & 0x01) == 1;
            if (strobe) {
                joyLatch = joy;
                long time = System.currentTimeMillis();
                if ((x_Pressed||y_Pressed) && time - X_Y_Pressed_start > X_Y_Pressed_Interval_Time) {
                    if (X_Y_Pressed&&x_Pressed) {
                        joy |= 0x01;
                    } else {
                        joy &= ~0x01;
                    }
                    if (X_Y_Pressed&&y_Pressed) {
                        joy |= 0x02;
                    } else {
                        joy &= ~0x02;
                    }
                    X_Y_Pressed_start = System.currentTimeMillis();
                    X_Y_Pressed = !X_Y_Pressed;
                }
                index = 0;
            }
            return joyIndex == JOY_2P;
        }
        return false;
    }

    public KeyAdapter getKeyAdapter() {
        return keyAdapter;
    }

    public static  void setButton(int jpIndex,int buttonIndex, int key) {
        joyPadIndex[jpIndex][buttonIndex] = key;
    }

    public static int getButton(int jpIndex,int buttonIndex) {
        return joyPadIndex[jpIndex][buttonIndex];
    }

    private class KeyAdapter extends java.awt.event.KeyAdapter implements Serializable{}
}
